/*
 * ﻿Copyright (C) 2009-2013 NewMain Softech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.newmainsoftech.gwetons.client.logging;

import java.util.Date;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayMixed;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.junit.client.GWTTestCase;
import com.newmainsoftech.gwetons.logging.client.LoggerInitializerForTestAccessor;
import com.newmainsoftech.gwetons.shared.logging.LoggerWrap;
import com.newmainsoftech.gwetons.shared.logging.LoggerWrapFactoryInstanceHolder;
import com.newmainsoftech.gwetons.shared.logging.LoggerWrapImpl;

public class GwtTest_JsniLoggerWrapImpl extends GWTTestCase {

	@Override
	public String getModuleName() {
	    return "com.newmainsoftech.gwetons.GWeTonsJUnit";
	}
		
	static class LogHandlerForTesting extends Handler {
		private String loggedMessage = "";
			String getLoggedMessage() {
				return loggedMessage;
			}
			void setLoggedMessage( String loggedMessage) {
				this.loggedMessage = loggedMessage;
			}

		@Override
		public void publish( LogRecord record) {
			if ( !isLoggable( record)) return;
			setLoggedMessage( getLoggedMessage() + "\n" + record.getMessage());
		}

		@Override
		public void flush() { // Do nothing
		}

		@Override
		public void close() { // Do nothing
		}
	}
	private LogHandlerForTesting logHandlerForTesting;
		LogHandlerForTesting getLogHandlerForTesting() {
			return logHandlerForTesting;
		}
	private LoggerWrap loggerWrap;
		LoggerWrap getLoggerWrap() {
			return loggerWrap;
		}
	private JsniLoggerWrapImpl jsniLoggerWrapImpl;
		JsniLoggerWrapImpl getJsniLoggerWrapImpl() {
			return jsniLoggerWrapImpl;
		}
		
	@Override
	public void gwtSetUp() {
		LoggerInitializerForTestAccessor.getLoggerInitializerForTestInstance().setupLogger();
		
		// Not using java.lang.Package because it is not in GWT's white list 
		// About logger for com.newmainsoftech.gwetons.shared.logging package
		String packageName = com.newmainsoftech.gwetons.shared.logging.LoggerWrap.class.getName();
			packageName = packageName.substring( 0, packageName.lastIndexOf( "."));
				assertEquals( 
						"%1$s may need to be updated since %2$s package seems to have been refactored."
						.replace( "%1$s", this.getClass().getName())
						.replace( "%2$s", "com.newmainsoftech.gwetons.shared.logging"),
						"com.newmainsoftech.gwetons.shared.logging", 
						packageName);
			Logger otherLogger = Logger.getLogger( packageName);
				otherLogger.setLevel( Level.INFO);
		// About logger for LoggerWrapImpl class
		otherLogger = Logger.getLogger( LoggerWrapImpl.class.getName());
			otherLogger.setLevel( Level.ALL);
		// About logger for com.newmainsoftech.gwetons.client.logging package
		packageName = com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap.class.getName();
			packageName = packageName.substring( 0, packageName.lastIndexOf( "."));
			assertEquals( 
					"%1$s may need to be updated since %2$s package seems to have been refactored."
					.replace( "%1$s", this.getClass().getName())
					.replace( "%2$s", "com.newmainsoftech.gwetons.client.logging"),
					"com.newmainsoftech.gwetons.client.logging", 
					packageName);
			otherLogger = Logger.getLogger( packageName);
				otherLogger.setLevel( Level.INFO);
		// About logger for JsniLoggerWrapImpl class
		otherLogger = Logger.getLogger( JsniLoggerWrapImpl.class.getName());
			otherLogger.setLevel( Level.ALL);
		
		logHandlerForTesting = new LogHandlerForTesting();
		
		loggerWrap 
		= LoggerWrapFactoryInstanceHolder.getLoggerWrapFactory().getLoggerWrap( this.getClass().getName());
			loggerWrap.getLogger().addHandler( getLogHandlerForTesting());
			loggerWrap.getLogger().setLevel( Level.ALL);
			
		jsniLoggerWrapImpl = new JsniLoggerWrapImpl();
			jsniLoggerWrapImpl.setLoggerWrap( getLoggerWrap());
	}
	@Override
	public void gwtTearDown() {
		getLoggerWrap().getLogger().removeHandler( getLogHandlerForTesting());
	}
	
	public void test_setLoggerWrap() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
			assertEquals( getLoggerWrap(), jsniLoggerWrapImpl.getLoggerWrap());
		
		LoggerWrap loggerWrapObj 
		= LoggerWrapFactoryInstanceHolder.getLoggerWrapFactory().getLoggerWrap( getName());
		jsniLoggerWrapImpl.setLoggerWrap( loggerWrapObj);
			assertEquals( loggerWrapObj, jsniLoggerWrapImpl.getLoggerWrap());
	}
	
	native JsArrayMixed generateJsArrayMixed() throws JavaScriptException /*-{
		var testParameters 
		= [ 
			[ "string:generateJsArrayMixed", "number:12345", "boolean:true", "undefined", 
				"object:Date(Sun Jun 10 2012 00:00:00 GMT-0700 (PDT))", "object:Array(arrayElement1,arrayElement2)", 
				"object:RegExp(/pattern/gi)", "function"], 
			"generateJsArrayMixed", 12345, true, , new Date( "Sun Jun 10 2012 00:00:00 GMT-0700 (PDT)"), 
			new Array( "arrayElement1", "arrayElement2"), /pattern/gi, function(){ alert( "Hello");}
			];
		return testParameters;
	}-*/;
	public void test_getType() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
			
		JsArrayMixed jsArrayMixed = generateJsArrayMixed();
		
			JsArrayString dataTypesArray = (JsArrayString)jsArrayMixed.getObject( 0);
			for( int index = 1; index < jsArrayMixed.length(); index++) {
				String dataStr = dataTypesArray.get( index - 1);
					String dataTypeStr = dataStr;
					String objectStr = null;
					if ( dataStr.contains( ":")) {
						dataTypeStr = dataStr.substring( 0, dataStr.indexOf( ":"));
						objectStr 
						= dataStr.substring( 
								dataStr.indexOf( dataTypeStr) + dataTypeStr.length() + ":".length());
					}
					
				String typeStr = jsniLoggerWrapImpl.getType( jsArrayMixed, index);
					assertEquals( dataTypeStr, typeStr);
				
					if ( "undefined".equals( typeStr)) {
						assertNull( jsArrayMixed.getObject(index));
					}
					else if ( "string".equals( typeStr)) {
						assertEquals( objectStr, jsArrayMixed.getString( index));
					}
					else if ( "number".equals( typeStr)) {
						assertEquals( 
								Double.valueOf( objectStr), 
								Double.valueOf( jsArrayMixed.getNumber( index))
								);
					}
					else if ( "boolean".equals( typeStr)) {
						assertEquals( 
								Boolean.valueOf( objectStr), 
								Boolean.valueOf( jsArrayMixed.getBoolean( index))
								);
					}
					else if ( "function".equals( typeStr)) {
						String dataElementStr = jsArrayMixed.getObject( index).toString().trim();
						assertTrue( 
								dataElementStr + " doesn't start with \"function\"",
								dataElementStr.startsWith( "function")
								);
					}
					else if ( "object".equals( typeStr)) {
						String dataElementStr = jsArrayMixed.getObject( index).toString();
						objectStr 
						= objectStr.substring( objectStr.indexOf( "(") + 1, objectStr.lastIndexOf( ")"));
						assertEquals( objectStr, dataElementStr);
					}
			} // for
	}
	
	// About entering method ----------------------------------------------------------------------
	public void test_LoggerWrap_entering() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		
		jsniLoggerWrapImpl.getLoggerWrap().entering( getName(), null);
		
		String capturedLog = logHandlerForTesting.getLoggedMessage();
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + getName() + "\"", 
					capturedLog.contains( getName())
					);
	}
	
	native JsArrayMixed callEntering( JsniLoggerWrapImpl jsniLoggerWrapImpl, String sourceMethod) 
	throws JavaScriptException /*-{
		var testMethodName 
		= this.@com.newmainsoftech.gwetons.client.logging.GwtTest_JsniLoggerWrapImpl::getName()();
		var testParameters 
		= this.@com.newmainsoftech.gwetons.client.logging.GwtTest_JsniLoggerWrapImpl::generateJsArrayMixed()();
		
		jsniLoggerWrapImpl.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrapImpl::entering(Ljava/lang/String;Lcom/google/gwt/core/client/JsArrayMixed;)(
			sourceMethod, testParameters);
		return testParameters;
	}-*/;
	
	void validateEnteringLog( JsArrayMixed jsArrayMixed) {
		String capturedLog = getLogHandlerForTesting().getLoggedMessage();
		String capturedLogCopy = capturedLog;
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + this.getClass().getName() + "\"", 
					capturedLogCopy.contains( this.getClass().getName())
					);
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + getName() + "\"", 
					capturedLogCopy.contains( getName())
					);
				capturedLogCopy 
				= capturedLogCopy.substring( capturedLogCopy.indexOf(getName()) + getName().length()); 
				
			JsArrayString dataTypesArray = (JsArrayString)jsArrayMixed.getObject( 0);
			for( int index = 1; index < jsArrayMixed.length(); index++) {
				String dataStr = dataTypesArray.get( index - 1);
					String dataTypeStr = dataStr;
					if ( dataStr.contains( ":")) {
						dataTypeStr = dataStr.substring( 0, dataStr.indexOf( ":"));
					}
					
					int capturedLogPointer = -1;
					String objectStr = (new Date()).toString();
					if ( "undefined".equals( dataTypeStr)) {
						objectStr = "null";
					}
					if ( "string".equalsIgnoreCase( dataTypeStr)) {
						objectStr = jsArrayMixed.getString( index);
					}
					else if ( "number".equalsIgnoreCase( dataTypeStr)) {
						objectStr = String.valueOf( jsArrayMixed.getNumber( index));
					}
					else if ( "boolean".equalsIgnoreCase( dataTypeStr)) {
						objectStr = String.valueOf( jsArrayMixed.getBoolean( index));
					}
					else if ( "function".equals( dataTypeStr)) {
						objectStr = jsArrayMixed.getObject( index).toString();
					}
					else if ( "object".equals( dataTypeStr)) {
						objectStr = jsArrayMixed.getObject( index).toString();
					}
					assertTrue( 
							"\"" + capturedLog + "\" does not contain \"" + objectStr + "\"", 
							capturedLogCopy.contains( objectStr)
							);
						capturedLogPointer 
						= capturedLogCopy.indexOf( objectStr) + objectStr.length();
					
					if ( capturedLogPointer < capturedLogCopy.length()) {
						capturedLogCopy = capturedLogCopy.substring( capturedLogPointer); 
					}
					else {
						capturedLogCopy = "";
					}
			} // for
	}
	public void test_entering() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		
		JsArrayMixed jsArrayMixed = callEntering( jsniLoggerWrapImpl, getName());
		
		validateEnteringLog( jsArrayMixed);
	}
	
	public void test_entering_withNullParams() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		
		jsniLoggerWrapImpl.entering( getName(), null);
		
		String capturedLog = getLogHandlerForTesting().getLoggedMessage();
		String capturedLogCopy = capturedLog;
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + this.getClass().getName() + "\"", 
					capturedLogCopy.contains( this.getClass().getName())
					);
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + getName() + "\"", 
					capturedLogCopy.contains( getName())
					);
				capturedLogCopy 
				= capturedLogCopy.substring( capturedLogCopy.indexOf(getName()) + getName().length()); 
	}
	
	native void callEnteringFunction( 
			JavaScriptObject enteringFunction, String sourceMethod, JsArrayMixed params) 
	throws JavaScriptException /*-{
		enteringFunction( sourceMethod, params);
	}-*/;
	
	public void test_getEnteringFunction() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		
		JavaScriptObject enteringFunction = jsniLoggerWrapImpl.getEnteringFunction();
		JsArrayMixed jsArrayMixed = generateJsArrayMixed();
		callEnteringFunction( enteringFunction, getName(), jsArrayMixed);
		
		validateEnteringLog( jsArrayMixed);
	}
	// --------------------------------------------------------------------------------------------
	
	// About log method ---------------------------------------------------------------------------
	native void callLogFunction( 
			JavaScriptObject logFunction, Level level, String message) 
	throws JavaScriptException /*-{
		logFunction( level, message);
	}-*/;
	public void test_getLogMethod() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		
		JavaScriptObject logFunction = jsniLoggerWrapImpl.getLogFunction();
		
		Level level = Level.WARNING;
		jsniLoggerWrapImpl.getLoggerWrap().getLogger().setLevel( Level.INFO);
		String message = "TEST Message";
		callLogFunction( logFunction, level, message);
			String capturedLog = getLogHandlerForTesting().getLoggedMessage();
				assertTrue( 
						"\"" + capturedLog + "\" does not contain \"" + message + "\"", 
						capturedLog.contains( message)
						);
		getLogHandlerForTesting().setLoggedMessage( "");
		jsniLoggerWrapImpl.getLoggerWrap().getLogger().setLevel( Level.SEVERE);
		callLogFunction( logFunction, level, message);
			capturedLog = getLogHandlerForTesting().getLoggedMessage();
			assertFalse( 
					"\"" + capturedLog + "\" contain \"" + message + "\"", 
					capturedLog.contains( message)
					);
	}
	// --------------------------------------------------------------------------------------------
	
	// About exiting methods ----------------------------------------------------------------------
	native void callExitingFunction( JavaScriptObject exitingFunction, String sourceMethod) 
	throws JavaScriptException /*-{
		exitingFunction( sourceMethod);
	}-*/;
	public void test_getExitingFunction() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		JavaScriptObject exitingFunction = jsniLoggerWrapImpl.getExitingFunction();
		callExitingFunction( exitingFunction, getName());
			String capturedLog = getLogHandlerForTesting().getLoggedMessage();
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + getName() + "\"", 
					capturedLog.contains( getName())
					);
	}
	native void callExitingWithResultFunction( 
			JavaScriptObject exitingWithResultFunction, String sourceMethod, Object result) 
	throws JavaScriptException /*-{
		
		exitingWithResultFunction( sourceMethod, result);
	}-*/;
	public void test_getExitingWithResultFunction() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		JavaScriptObject exitingWithResultFunction = jsniLoggerWrapImpl.getExitingWithResultFunction();
		String message ="TEST message";
		callExitingWithResultFunction( exitingWithResultFunction, getName(), message);
		String capturedLog = getLogHandlerForTesting().getLoggedMessage();
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + getName() + "\"", 
					capturedLog.contains( getName())
					);
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + message + "\"", 
					capturedLog.contains( message)
					);
	}
	
	native JsArrayString callExitingWithResultFunction( 
			JavaScriptObject exitingWithResultFunction, String sourceMethod) 
	throws JavaScriptException /*-{
		var arrayObj = new Array();
		
//		exitingWithResultFunction( sourceMethod, notExistingVariable);
//			arrayObj.push( "undefined");
			
		var resultString = "Test Result String";
		exitingWithResultFunction( sourceMethod, resultString);
			arrayObj.push( "string:" + resultString);
			
		var resultNumber = 12345;
		exitingWithResultFunction( sourceMethod, resultNumber);
			arrayObj.push( "number:" + String( resultNumber));
		
		var resultBoolean = true;
		exitingWithResultFunction( sourceMethod, resultBoolean);
			arrayObj.push( "boolean:" + String( resultBoolean));
			
		var resultFunction = function(){ alert( "Hello");};
		exitingWithResultFunction( sourceMethod, resultFunction);
			arrayObj.push( "function:" + String( resultFunction));
		
		var resultDate = new Date( "Sun Jun 10 2012 00:00:00 GMT-0700 (PDT)");
		exitingWithResultFunction( sourceMethod, resultDate);
			arrayObj.push( "date:" + String( resultDate));
		
		var resultArray = new Array( "arrayElement1", "arrayElement2");
		exitingWithResultFunction( sourceMethod, resultArray);
			arrayObj.push( "array:" + String( resultArray));
			
		var resultRegExp = /pattern/gi;
		exitingWithResultFunction( sourceMethod, resultRegExp);
			arrayObj.push( "regexp:" + String( resultRegExp));
			
		var resultNull = null;
		exitingWithResultFunction( sourceMethod, resultNull);
			arrayObj.push( "object:" + String( resultNull));
		return arrayObj;
	}-*/;
	public void test_getExitingWithResultFunction_with_JavaScript_Native_Objects() {
		JsniLoggerWrapImpl jsniLoggerWrapImpl = getJsniLoggerWrapImpl();
		JavaScriptObject exitingWithResultFunction = jsniLoggerWrapImpl.getExitingWithResultFunction();
		
		JsArrayString resultJsArray = callExitingWithResultFunction( exitingWithResultFunction, getName());
		
		String capturedLog = getLogHandlerForTesting().getLoggedMessage();
			assertTrue( 
					"\"" + capturedLog + "\" does not contain \"" + getName() + "\"", 
					capturedLog.contains( getName())
					);
			String capturedLogCopy = capturedLog;
			for( int index = 0; index < resultJsArray.length(); index++) {
				String resultStr = resultJsArray.get( index);
//					String resultType = resultStr.substring( 0, resultStr.indexOf( ":"));
					resultStr = resultStr.substring( resultStr.indexOf( ":") + 1);
					
				assertTrue( 
						"\"" + capturedLog + "\" does not contain \"" + resultStr + "\"", 
						capturedLogCopy.contains( resultStr)
						);
				capturedLogCopy 
				= capturedLogCopy.substring( capturedLogCopy.indexOf( resultStr) + resultStr.length());
			} // for
	}
	// --------------------------------------------------------------------------------------------
}
